package com.example.houselease.service.impl;

import com.alibaba.fastjson.JSON;
import com.example.houselease.mapper.AdminMapper;
import com.example.houselease.mapper.RoleMapper;
import com.example.houselease.pojo.dto.admin.AdminAddNewDTO;
import com.example.houselease.pojo.dto.admin.AdminLoginDTO;
import com.example.houselease.pojo.dto.admin.AdminRoleDTO;
import com.example.houselease.pojo.dto.admin.AdminUpdateDTO;
import com.example.houselease.pojo.entity.Admin;
import com.example.houselease.pojo.vo.*;
import com.example.houselease.repo.IAdminRedisRepository;
import com.example.houselease.security.AdminDetails;
import com.example.houselease.service.IAdminService;
import com.example.houselease.util.ErrorUtils;
import com.example.houselease.web.ServiceCode;
import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.stream.Collectors;

/**
 * 管理员service 实现类
 * @author sf
 * @version 0.0.1
 * */
@Service
@Slf4j
@Transactional
public class IAdminServiceImpl implements IAdminService {

    @Autowired
    private AuthenticationManager authenticationManager;

    @Autowired
    IAdminRedisRepository adminRedisRepository;

    @Autowired
    private AdminMapper adminMapper;

    @Autowired
    private RoleMapper roleMapper;

    @Value("${house-lease.jwt.duration-in-minute}")
    private Long date;

    @Value("${house-lease.jwt.secretKey}")
    private String secretKey;

    @Autowired
    private PasswordEncoder passwordEncoder;

    @Override
    public String login(AdminLoginDTO adminLoginDTO) {
        log.debug("开始处理【管理员登录】业务，参数：{}",adminLoginDTO);
        //创建认证对象
        Authentication authentication = new UsernamePasswordAuthenticationToken
                (adminLoginDTO.getUsername(),adminLoginDTO.getPassword());
        //开始验证用户名和密码
        Authentication authenticationResult = authenticationManager.authenticate(authentication);
        log.debug("认证通过，认证结果：{}", authenticationResult);
        log.debug("认证通过，认证结果中的当事人：{}", authenticationResult.getPrincipal());

        //将用户数据写入JWT
        AdminDetails principal = (AdminDetails) authenticationResult.getPrincipal();
        Map<String,Object> claims = new HashMap();
        claims.put("id",principal.getId());
        claims.put("username",principal.getUsername());
        //获取权限列表
        Collection<GrantedAuthority> authorities = principal.getAuthorities();
        //转换成json字符串
        String authoritiesJsonString = JSON.toJSONString(authorities);
        claims.put("authoritiesJsonString",authoritiesJsonString);

        //生成JWT
        String jwt = Jwts.builder()
                .setHeaderParam("alg","HS256")
                .setHeaderParam("typ","JWT")
                .setClaims(claims)
                .setExpiration(new Date(System.currentTimeMillis()+this.date*60*1000))
                .signWith(SignatureAlgorithm.HS256, this.secretKey)
                .compact();
        log.debug("响应的jwt数据：{}",jwt);
        return jwt;
    }

    @Override
    public List<AdminListVO> list() {
//        List<AdminListVO> adminListVOS = adminMapper.select();
        log.debug("开始【从redis读取管理员列表】");
        return adminRedisRepository.list();
    }

    @Override
    public void update(Long id, AdminUpdateDTO adminUpdateDTO) {
//        AdminDetailVO adminDetailVO = adminMapper.selectById(id);
        AdminDetailVO adminDetailVO = adminRedisRepository.get(id);
        ErrorUtils.isNull(adminDetailVO,"修改失败，该数据不存在");
        Admin admin = new Admin();
        BeanUtils.copyProperties(adminUpdateDTO,admin);
        admin.setId(id);
        int i = adminMapper.update(admin);
        ErrorUtils.isFail(i,ServiceCode.ERR_UPDATE);
    }

    @Override
    public void addNew(AdminAddNewDTO adminAddNewDTO) {
        log.debug("开始处理【添加管理员】的业务，参数：{}", adminAddNewDTO);
        {
            int count = adminMapper.countByUsername(adminAddNewDTO.getUsername());
            ErrorUtils.isExists(count,"添加管理员失败，尝试使用的用户名已经被占用！");
        }
        {
            String phone = adminAddNewDTO.getPhone();
            int count = adminMapper.countByPhone(phone);
            ErrorUtils.isExists(count,"添加管理员失败，尝试使用的手机号码已经被占用！");
        }
        {
            String email = adminAddNewDTO.getEmail();
            int count = adminMapper.countByEmail(email);
            ErrorUtils.isExists(count,"添加管理员失败，尝试使用的电子邮箱已经被占用！");
        }
        Admin admin = new Admin();
        BeanUtils.copyProperties(adminAddNewDTO,admin);//数据转换
        admin.setPassword(passwordEncoder.encode(admin.getPassword()));//密码加密
        int insert = adminMapper.insert(admin);//添加管理员数据
        ErrorUtils.isFail(insert,ServiceCode.ERR_INSERT);
        int i = adminMapper.insertRole(admin.getId(),adminAddNewDTO.getRoleIds());
        ErrorUtils.isFail(i,ServiceCode.ERR_INSERT);
    }

    @Override
    public List<Long> getRoleByAdminId(Long id) {
        AdminDetailVO adminDetailVO = adminMapper.selectById(id);
        ErrorUtils.isNull(adminDetailVO,"操作失败，该管理员数据不存在");
        List<RoleDetailVO> roleDetailVOS = adminMapper.selectRoleByAdminId(id);
        List<Long> ids = new ArrayList<>();
        for (RoleDetailVO r : roleDetailVOS){
            ids.add(r.getId());
        }
        return ids;
    }

    @Override
    public void updateAdminRoleByAdminId(Long adminId, List<Long> roleIds) {
        log.debug("开始处理【修改管理员拥有的角色】业务，参1：{}，参数2：{}",adminId,roleIds);
//        AdminDetailVO adminDetailVO = adminMapper.selectById(adminId);
        AdminDetailVO adminDetailVO = adminRedisRepository.get(adminId);
        ErrorUtils.isNull(adminDetailVO,"修改失败，该管理员数据不存在");
        //数据库原先管理员拥有的角色列表(包含禁用的管理员角色)
        List<AdminRoleListVO> oldAdminRoles = adminMapper.selectRolesByAdminId(adminId);
        List<Long> oldRoleIds = new ArrayList<>();
        oldAdminRoles.forEach(i -> oldRoleIds.add(i.getRoleId()));
        Collections.sort(oldRoleIds);
        Collections.sort(roleIds);
        log.debug("数据库查询到的，原先拥有的角色id数据：{}",oldRoleIds);
        //判断用户传递的数据和数据库查询到的是否一致
        if (!roleIds.equals(oldRoleIds)){
            //获取要添加的，数据库中未拥有的管理员角色id列表。（用户传递的数据，跟数据库原有的去重）
            List<Long> enableIds = roleIds.stream().filter(item -> !oldRoleIds.contains(item))
                    .collect(Collectors.toList());
            //获取要禁用的，管理员拥有的角色id列表。（数据库原有的数据，跟用户传递的去重）
            List<Long> disableIds = oldRoleIds.stream().filter(item -> !roleIds.contains(item))
                    .collect(Collectors.toList());
            if (enableIds.size()!=0){
                log.debug("要插入的数据库没有的管理员角色数据：{}",enableIds);
                this.enableAdminRoleByRoleIds(adminId,enableIds); //添加管理员拥有的新角色数据
            }
            if (disableIds.size()!=0){ //判断是否有要禁用的管理员角色数据
                log.debug("要修改的数据库已有的角色数据：{}",disableIds);
                this.disableAdminRoleByRoleIds(adminId,disableIds);
            }
        }else {
            //设置管理员拥有的角色状态为启用
            log.debug("开始处理数据原有的管理员角色数据");
            oldAdminRoles.forEach(
                    i -> {if (i.getEnable()==0) adminMapper.updateAdminRole(adminId,i.getRoleId());});
        }
    }

    @Override
    public void deleteById(Long id) {
        log.debug("开始处理【删除管理员】业务，参数：{}",id);
//        AdminDetailVO adminDetailVO = adminMapper.selectById(id);
        AdminDetailVO adminDetailVO = adminRedisRepository.get(id);
        ErrorUtils.isNull(adminDetailVO,"删除失败，该管理员数据不存在");
        int i = adminMapper.deleteById(id);
        ErrorUtils.isFail(i,ServiceCode.ERR_DELETE);
    }

    @Override
    public void enable(Long id) {
        log.debug("开始处理【启用管理员】业务，参数：{}",id);
        AdminDetailVO adminListVO = adminRedisRepository.get(id);
        ErrorUtils.isNull(adminListVO,"启用失败，该管理员数据不存在");
        int i = adminMapper.updateAdminEnable(id);
        ErrorUtils.isFail(i,ServiceCode.ERR_UPDATE);
    }

    @Override
    public void disable(Long id) {
        log.debug("开始处理【禁用管理员】业务，参数：{}",id);
//        AdminDetailVO adminDetailVO = adminMapper.selectById(id);
        AdminDetailVO adminDetailVO = adminRedisRepository.get(id);
        ErrorUtils.isNull(adminDetailVO,"禁用失败，该管理员数据不存在");
        int i = adminMapper.updateAdminDisable(id);
        ErrorUtils.isFail(i,ServiceCode.ERR_UPDATE);
    }

    @Override
    public AdminDetailVO getAdminDetail(Long id) {
        log.debug("开始处理【管理员详情】业务，参数：{}",id);
//        AdminDetailVO adminDetailVO = adminMapper.selectById(id);
        AdminDetailVO adminDetailVO = adminRedisRepository.get(id);
        ErrorUtils.isNull(adminDetailVO,"修改失败，该管理员数据不存在");
        return adminDetailVO;
    }

    @Override
    public void rebuildCache() {
        log.debug("开始处理【重建管理员缓存】的业务，无参数");
        adminRedisRepository.deleteAll();//先删除redis中的缓存
        List<AdminListVO> adminListVOS = adminMapper.select();
        adminRedisRepository.save(adminListVOS);//添加管理员列表数据到redis
        List<AdminDetailVO> adminDetailVOS = new ArrayList<>();
        adminListVOS.forEach(i -> adminDetailVOS.add(adminMapper.selectById(i.getId())));
        adminDetailVOS.forEach(i -> adminRedisRepository.save(i));//添加每个管理员详情数据到redis
    }

    private void disableAdminRoleByRoleIds(Long adminId, List<Long> disableIds) {
        for (Long id : disableIds){
            RoleDetailVO roleDetailVO = roleMapper.selectById(id);
            ErrorUtils.isNull(roleDetailVO,"操作失败，角色数据不存在，请刷新页面，重新尝试");
        }
        int i = adminMapper.updateAdminRoleByRoleIds(adminId, disableIds);
        ErrorUtils.isFail(i,ServiceCode.ERR_UPDATE);
    }

    private void enableAdminRoleByRoleIds(Long adminId, List<Long> enableIds) {
        for (Long id : enableIds){
            RoleDetailVO roleDetailVO = roleMapper.selectById(id);
            ErrorUtils.isNull(roleDetailVO,"操作失败，角色数据不存在，请刷新页面，重新尝试");
        }
        int i = adminMapper.insertRoleByRoleIds(adminId, enableIds);
        ErrorUtils.isFail(i,ServiceCode.ERR_INSERT);
    }
}
